package org.NooLab.docserver.main;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.TreeMap;
import java.util.Vector;

import org.NooLab.docobserver.structures.DocObservation;
import org.NooLab.docobserver.structures.DocumentObserverIntf;
import org.NooLab.docserver.component.WatchedFile;
import org.NooLab.docserver.properties.ControlSettingsSection;
import org.NooLab.docserver.properties.DocoControlProperties;
import org.NooLab.docserver.properties.DocumentObserverProperties;
import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.files.DirectoryContent;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.net.GUID;
import org.NooLab.utilities.strings.StringsUtil;


/**
 * 
 * this class represent the "observer" of a single folder, without its sub-directories
 * 
 * 
 * 
 *
 */
public class DocumentObserverFolderCore extends DocumentObserverCoreAbstract {

	String observedDir;
		
	public boolean changed;
	public int consoleMsg;
	
	
	// ========================================================================
	public DocumentObserverFolderCore( DocumentObserver parent, String observeddir, int ix) throws Exception{
	
		super( parent, ix);
		
		
		observesDirectories = true;
		
		observedDir = observeddir ;
		observedDir = StringsUtil.replaceall(observedDir, "\\", "/") ;
				
		out.setPrefix("[Observer(folder)-"+index+"]") ;
	}
	// ========================================================================
	

	public boolean prepare( String dir, boolean subDirs, DocoControlProperties dcp ) throws FileNotFoundException{
	
		boolean rB= false;
		
		if ((dir.length()==0) || (parent.fileutil.direxists(dir)==false)){
			dir = observedDir ;
		}
		
		File fdir = new File(dir);
		if (!fdir.exists()){
			throw new FileNotFoundException(dir);
			 
		}
		
		try{

			
			docoControlProperties = dcp; 
			observedDir = fdir.getAbsolutePath() ;
			 
			
			observerProc = new ObserverProcess();
			
			

			if (watchedFiles.size()>0){
				rB=true;
			}
			
			if (consoleMsg>0){
				parent.out.print(2,"Observation process has been prepared for folder <"+ observedDir+">");
			}

		}catch(Exception e){
			
		}
		return rB;
	}
	


	
	
	public void start(){
		
		
		out.print(2, "DocumentObserver core starting ...") ;
		
		docoDataBase = docoFactory.getDocoDataBase() ;
		
		
		try{
			
			
			// reset "changed" - flag to false before starting thread
			resetallFlags() ;
			
			observerProc.start();
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
	}

	
	private ArrayList<WatchedFile> setListOfWatchedFolders( ArrayList<String> foldernames ) {

		// watchedFiles ;
		
		// first get the content of the directory before starting to watch;
		WatchedFile wF ;
		ArrayList<WatchedFile> watchedFiles = new ArrayList<WatchedFile>();
		
		boolean hb;
		String fplain,str  ;
		String filename ;
		ArrayList<String> flist, controlExt;
		File _fil ;	
		
		ControlSettingsSection exclLoc = docoControlProperties.getLocationExcluded();
		ControlSettingsSection inclLoc = docoControlProperties.getLocationIncluded();

		
		flist = new ArrayList<String>(foldernames);
		
		// 
		for (int i=0;i<flist.size();i++){
			
			filename = flist.get(i);
			try{
				_fil = new File(filename);
				hb = true;
				
				// check again: exclLoc, inclLoc
				
				if (hb){
					wF = new WatchedFile();

					wF.setLDate(_fil.lastModified());
					wF.setFilename(filename);
					wF.setFilesize(_fil.length());
					wF.setHomeDir(this.observedDir);

					watchedFiles.add(wF);
				}
			}catch(Exception e){
				
			}
			
			
		} // i->

		return watchedFiles;
	}
	
	
	
	

	public Vector<WatchedFile> getObservedFiles(){
		
		Vector<WatchedFile> wafs = new Vector<WatchedFile>();

		try{
			loadWatches();
			
		}catch(FileNotFoundException e){
			
		}
		
		// remove those to be deleted
		
		// 
		return wafs;
	}
	
	protected void loadWatches() throws FileNotFoundException{
		
		
		try{
			if (observedDir!=null){
					
				getDirContent( observedDir ) ;
				
			}
			
		}catch( FileNotFoundException e){
			throw new FileNotFoundException(observedDir);
		}
		
	}
	
	private void getDirContent( String pathStr) throws FileNotFoundException{

		Object obj=null;
		long lDate;
		int p,dd;
		File[] csdFiles ; 
		File currDir,_file ;
	    String tmpstr, rStr="",_pathStr,path_of_file = "";
	    boolean consider ,x,_dirmatch;
	    WatchedFile wF ;


	    
	    currDir = new File( pathStr ) ;
	    currDir = new File( currDir.getAbsolutePath() );
	    csdFiles = currDir.listFiles();
	     
	    if ((csdFiles==null)){
	    	throw new FileNotFoundException();
	    }

	    // if we have deleted a file, we will not see it in the 
	    // following loop of course
	    // we have to detect del event indirectly by using a flag for wF
	    // and the to check those not treated
	    WatchedFile  pwf ;
	     

	    _pathStr = pathStr.replace("/", "\\");
	    _pathStr = _pathStr.replace(":\\", "$$$\\");
	    _pathStr = _pathStr.replace(":", "\\");
	    _pathStr = _pathStr.replace("$$$\\", ":\\");
	    		    	
	    
	    for ( int w=0;w< watchedFiles.size();w++){
	    	pwf = watchedFiles.get(w);
	    	if (pwf.getInternalFlag()<=-3){
	    		continue;
	    	}
	    	path_of_file = pwf.getHomeDir(); // DFutils.createPath( , pwf.getFilename());
	    	
	    	p = pwf.getFilename().lastIndexOf("\\");
	    	if (p>=0){
	    		path_of_file = path_of_file.substring(0, p);   
	    	}
	    	
	    	// reset only those which are in current dir pathStr
	    	_dirmatch = path_of_file.contentEquals(_pathStr);
	    	
	    	if (_dirmatch==true){
		    	pwf.setInternalFlag(-1); // necessary to detect DEL
		    	pwf.setObservation("");
		    	pwf.setRemoveThis(-1);
		    	watchedFiles.set(w,pwf) ;
	    	}
	    } // w -> watchedFiles
	    	
		if ((csdFiles!=null) && (csdFiles.length>0)){

		    for (File _fil : csdFiles){
		    	
		    	consider = false;
		    	tmpstr = _fil.getAbsolutePath(); //  .getName();	
		    	tmpstr = StringsUtil.getExtensionFromFilename(tmpstr) ;
		    	consider = somePatternmatches(tmpstr) ; // we have to test all found files here too, since 
		    											// files could be added ...	
		    	if (consider){
		    		consider = _fil.isFile() ;
		    	}
		    	
		    	
		    	
		    	if (  consider==true)  {
		    		    	
		    		wF = new WatchedFile() ;
		    		
		    		wF.setLDate(_fil.lastModified()) ;
		    		wF.setFilename( _fil.getName() ) ;
		    		wF.setFilesize(_fil.length()) ;
		    		wF.setHomeDir(path_of_file) ;
			    	
			    	// if (consider==true)
			    	{
			    		wF.setObservation("");
			    	    
			    		dd = isDifferent(wF) ;
			    		
			    		p = isKnownbyName(wF);
			    		
			    		if ( ((dd<=0) && (p>=0)) ){
			    			wF.setInternalFlag( DocumentObserverIntf._FILE_CHANGE_NIL) ;
			    			wF.setObservation("NIL") ; 
			    			
			    			 
			    			if (startup>0){
			    				
			    				watchedFiles.add(wF);
			    			}
			    			else{
			    				watchedFiles.set(p,wF) ;
			    			}
			    		}

			    		if (startup>=0)
			    		{
			    			if ((p<0) || (dd>0)){
				    								// isDifferent(wF), see above
				    			wF.setInternalFlag( DocumentObserverIntf._FILE_CHANGE_TMP) ;
				    			
					    		if (p<0){
					    			wF.setChanged(true) ;
					    			wF.setInternalFlag(DocumentObserverIntf._FOLDER_CHANGE_NEW) ;
					    			wF.setObservation("NEW") ;
					    			watchedFiles.add(wF) ;
					    			changedWFs.add(wF);
					    		}
					    		else{
					    			wF.setChanged(true) ;
					    			wF.setInternalFlag(DocumentObserverIntf._FOLDER_CHANGE_RENAME) ;
					    			wF.setObservation("MOD") ; 
					    			watchedFiles.set(p,wF) ;
					    			changedWFs.add(wF);
					    		}
				    			
				    		}
				    		else{
				    			if (p<0){
				    				wF.setInternalFlag( DocumentObserverIntf._FILE_CHANGE_UNKNOWN ) ;
				    				watchedFiles.set(p,wF) ;
				    			}
				    		}
			    		} // startup=0 ?
			    				    		
			    	} // consider this file ? -> after checking for dirmatch

		    	} // consider
		    } // for all files
				
			
		} // csdFiles ?
		
	    if (startup==0){
			for ( int w=0;w< watchedFiles.size();w++){
				pwf = watchedFiles.get(w);
			    	
		    	// tmpstr = pathStr + "/" + pwf.filename;
		    	// _file = new File(pathStr);
		    	
		    	/*  VERY STRANGE behavior: after deleting a file to the recycle bin
		    	 *  exists() still returns true, while after 
		    	 *  csdFiles = currDir.listFiles();
		    	 *  csdFiles does not know about the file !!! 
		    	 */
		    	p = Filelistcontains( csdFiles, pwf.getFilename()) ;
		    	
		    	// we have to check ONLY the files of the currently checked directory
if (startup==0){
	startup=0;
}		    	/*
                _pathStr = pathStr.replace("/", "\\");
                _pathStr = _pathStr.replace(":\\", "$$$\\");
                _pathStr = _pathStr.replace(":", "\\");
                _pathStr = _pathStr.replace("$$$\\", ":\\");
		    	*/
		    	_dirmatch = pwf.getHomeDir().contains(_pathStr);
		    	
		    	
		    	if ((startup==0) && (_dirmatch) && ( (p<0) && (pwf.getInternalFlag() < 0))){
		    		p=0;
		    		
		    		pwf.setRemoveThis(-3);
		    		pwf.setChanged(true) ;
		    		pwf.setInternalFlag( DocumentObserverIntf._FOLDER_CHANGE_DEL ) ;
		    		pwf.setObservation("DEL") ; 
	    			watchedFiles.set(w,pwf) ;
	    			
	    			changedWFs.add(pwf) ;
		    		
		    	}
		    	_file=null;
		    }	    	
	    } // startup==0 ?

	   
	}

	private int Filelistcontains( File[] csdFiles, String filename ){
		int rval = -1, p=-1;
		
		for (File f: csdFiles){
			p++;
			
			if (f.getName().contentEquals(filename)){
				rval = p;
				break ;
			}
		}
		
		return rval;
	}
	
	
	private boolean somePatternmatches(String str){
		boolean rb=false;
		String pattern;
		
		if (patterns.size()==0){
			rb = true ;
		}
		else{
			str = str.toLowerCase(); // .contains(pattern) )
			rb = true ; // loopi
			parent.docoControlProperties.getExcludedExtensions();
			parent.docoControlProperties.getIncludedExtensions(); // TODO ... types, patterns
		}
		return rb;
	}



	public String getObservedDir() {
		observedDir = StringsUtil.replaceall(observedDir, "\\", "/") ;
		return observedDir;
	}

	
	
}
